package org.hive.managers
{
	import flash.utils.Dictionary;
	
	import org.hive.Hive;
	import org.hive.events.*;
	import org.hive.sequence.*;
	import org.hive.state.*;

	public class SequenceManager extends Manager
	{

		private var __SEQUENCES:Dictionary = new Dictionary( );
		private var __SEQUENCES_BY_NAME:Dictionary = new Dictionary( );
		private var __CURRENT_SEQUENCE:Sequence;
		private var __LAST_SEQUENCE:Sequence;
		
		
       
		
		//_________________________________________________________________________________________________ PRIVATE PROPERTIES

        private static var __activeSequenceDelays   : Dictionary = new Dictionary( false );
		private static var __cancelList             : Dictionary=new Dictionary();

		
		protected var actionLib : Dictionary = new Dictionary(false);
		protected var tweenableActionLib : Dictionary = new Dictionary(false);
		
		
		
		
		//_________________________________________________________________________________________________ PUBLIC METHODS

		
        /**
        *
        * @param	target
        */
		public function SequenceManager( hive:Hive )
		{
			super(hive);
			
		}

		
		 /**
        *
        * @param	sequence
        * @return
        */
		public function add( sequence:Sequence ):Sequence
		{
            if( __SEQUENCES[ __SEQUENCES_BY_NAME[ sequence.name ] ] )
            delete __SEQUENCES[ __SEQUENCES_BY_NAME[ sequence.name ] ];

			__SEQUENCES_BY_NAME[ sequence.name ] = sequence;
			__SEQUENCES[ sequence ] = sequence.name;
			
			return sequence;
		}
		
        /**
        *
        * @param	sequence
        * @return
        */
		public function remove( sequence:Sequence ):Sequence
		{
		    __SEQUENCES[ sequence ] = null;
			delete __SEQUENCES[ sequence ];
			delete __SEQUENCES_BY_NAME[ sequence.name ];
			return sequence;
		}
		
        /**
        *
        * @param	name
        * @return
        */
		public function removeByName( name:String ):Sequence
		{
			return remove( getSequenceByName( name ) );
		}
		
        /**
        *
        * @param	name
        * @return
        */
		public function getSequenceByName( name:String ):Sequence
		{
			return __SEQUENCES_BY_NAME[ name ];
		}
		
        /**
        *
        * @param	sequence
        */
		public function set currentSequence( sequence:Sequence ):void
		{
			__LAST_SEQUENCE = __CURRENT_SEQUENCE;
			__CURRENT_SEQUENCE = sequence;
		}

        /**
        *
        * @return
        */
		public function get currentSequence( ):Sequence
		{
			return __CURRENT_SEQUENCE;
		}
		
        /**
        *
        * @param	sequence
        */
		public function set lastSequence( sequence:Sequence ):void
		{
			__LAST_SEQUENCE = sequence;
		}
		public function get lastSequence( ):Sequence
		{
			return __LAST_SEQUENCE;
		}
		
				
		/**
        *
        * @param	definition		Registers the class definition to the action library.
        * @param	callback		Registers the callback function with the class definition: callback( action:*, sequence:Sequence, positionInSequence:int ).
		* @param	tweenable		Use Tweener if true.
        */
		
		public function registerAction( definition:Class, callback:Function, tweenable:Boolean=false ):void
		{
			if ( tweenable )
				tweenableActionLib[ definition ] = callback;
			else
				actionLib[ definition ] = callback;
		}
		
		/**
        *
        * @param	definition		Unregisters the class definition from the action library.
		* @param	tweenable		Unregisters tweenable action if true.
        */
		
		public function unregisterAction( definition:Class, tweenable:Boolean=false ):void
		{
			if ( tweenable )
				delete tweenableActionLib[ definition ];
			else
				delete actionLib[ definition ];
		}
		
        /**
        *
        * @param	sequence		Instance of sequence to play.
        * @return
        */
		public function playSequence( sequence:Sequence ):void
		{
			if(sequence){
				if(Hive.DEBUG_MODE){trace('StateManager :: playSequence : ' + sequence.name)}
				if( !sequence.active ){
	                currentSequence = add( sequence );
					sequence.activate();
				}
	
	            //if( !sequence.hasEventListener( SequenceEvent.ON_COMPLETE ) && sequence is ActionSequence )
	            //sequence.addEventListener( SequenceEvent.ON_COMPLETE, onManagerChanged, false, 0, true );

				for ( var i : Number = 0; i < sequence.length; i++ ) {
					var action			:* = sequence.index(i);
					var scope			:*;
					var actionPlayed	:Boolean = false;

					for ( var definition:* in actionLib ) {
						if ( action is definition ) {
							actionLib[ definition ]( action, sequence, i );
							actionPlayed = true;
						}
					}
					
					if( action is SequenceDelay )
	                {
	                    //if( (i + 1) < sequence.length )
	                   // sequence.removeEventListener( SequenceEvent.ON_COMPLETE, onManagerChanged );
	
	                    if( !__activeSequenceDelays[ sequence.name ] )
	                    __activeSequenceDelays[ sequence.name ] =  new Dictionary( );
	
	                    __activeSequenceDelays[ sequence.name ][ action ] = i;
	
						action.start( onExecuteResumed, sequence.slice( ++i ) );
						return;
					} 
					else if( action is SequenceFunction )
	                {
	                    var sequenceFunction:SequenceFunction = action;
	                    sequenceFunction.method.apply( sequenceFunction.scope, sequenceFunction.argumentsList );
	                }
					else if ( action as Object && action.hasOwnProperty("scope") )
					{
						for ( var conditionalClass:* in tweenableActionLib ) {
							if ( action.scope as conditionalClass != null ) {
								tweenableActionLib[ conditionalClass ]( action, sequence, i );
							}
						}
					}
					else if( action is Function ){
					    action.apply( action, sequence.getParams(action) );
					}
					else
					{
						if ( !actionPlayed )
						{
							if(Hive.DEBUG_MODE){trace("StateManager :: playSequence : Action definition not registered, " + action)}
						}
					}
				}
	
	            if( sequence is CloseSequence ) hive.stateManager.executeStateSwitch( );
	            else sequence.complete = true;
	            
	            if( sequence.complete ){
	                remove(sequence);
	            }
				
			}else {
				if(Hive.DEBUG_MODE){trace("StateManager :: playSequence : Sequence is null. ")}
			}
		}
		
		/**
        *
        * @param	name
        * @return
        */
		public function cancelSequence( name:String ):Boolean
		{
            //trace('StateManager :: cancelSequence : '+name);

			if( name is String ){
				__cancelList[ name ] = true;
                executeCancelSequence( name );
                remove(getSequenceByName(name)); // TODO: test cancel sequence
				return true;
			}
			return false;
		}
		
		//_________________________________________________________________________________________________ PRIVATE METHODS


        /**
        *
        * @param	name
        * @return
        */
        private function executeCancelSequence( name:String ):Boolean
        {
           //trace('StateManager :: executeCancelSequence : '+name);

            var sequence:Sequence;

            if( __cancelList[ name ] &&
                ( sequence = getSequenceByName( name )) != null ){

                sequence.kill( );

                for( var delay:* in __activeSequenceDelays[ name ] ){
                    delay.stop( );
                    delete __activeSequenceDelays[ name ][ delay ];
                }

                delete __cancelList[ name ];

                var event:SequenceEvent = new SequenceEvent( SequenceEvent.ON_CANCEL );
                sequence.dispatchEvent( event );

                return true;
            }
            else return false;
        }
		
        /**
        *
        * @param	e
        * @return
        */
		private function onExecuteResumed( e:SequenceDelayEvent ):void
		{
            delete __activeSequenceDelays[ e.sequence.name ];

            if( !e.sequence.length ) 
            {
                remove(e.sequence);
                return;
            }

			if( __cancelList[ e.sequence.name ] ) {
                executeCancelSequence( e.sequence.name );
				return;
			}
			else playSequence( e.sequence );
		}
		
	}
}